// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
typealias @ffi.XExternString

///|
typealias @ffi.XExternByteArray

///|
typealias @ffi.XExternStringArray

///|
fn get_error_message_ffi() -> XExternString = "__moonbit_fs_unstable" "get_error_message"

///|
fn get_error_message() -> String {
  @ffi.string_from_extern(get_error_message_ffi())
}

///|
fn get_file_content_ffi() -> XExternByteArray = "__moonbit_fs_unstable" "get_file_content"

///|
fn get_dir_files_ffi() -> XExternStringArray = "__moonbit_fs_unstable" "get_dir_files"

///|
fn read_file_to_bytes_ffi(path : XExternString) -> Int = "__moonbit_fs_unstable" "read_file_to_bytes_new"

///|
fn write_bytes_to_file_ffi(
  path : XExternString,
  content : XExternByteArray,
) -> Int = "__moonbit_fs_unstable" "write_bytes_to_file_new"

///|
fn read_file_to_bytes_internal(path : String) -> Bytes raise IOError {
  let res = read_file_to_bytes_ffi(@ffi.string_to_extern(path))
  guard res != -1 else { raise IOError(get_error_message()) }
  @ffi.byte_array_from_extern(get_file_content_ffi())
}

///|
fn read_file_to_string_internal(
  path : String,
  encoding~ : String = "utf8",
) -> String raise IOError {
  guard encoding == "utf8" else {
    raise IOError(
      "Unsupported encoding: \{encoding}, only utf8 is supported for now",
    )
  }
  @ffi.utf8_bytes_to_mbt_string(read_file_to_bytes_internal(path))
}

///|
fn write_bytes_to_file_internal(
  path : String,
  content : Bytes,
) -> Unit raise IOError {
  let res = write_bytes_to_file_ffi(
    @ffi.string_to_extern(path),
    @ffi.byte_array_to_extern(content),
  )
  guard res != -1 else { raise IOError(get_error_message()) }
}

///|
fn write_string_to_file_internal(
  path : String,
  content : String,
  encoding~ : String = "utf8",
) -> Unit raise IOError {
  guard encoding == "utf8" else {
    raise IOError(
      "Unsupported encoding: \{encoding}, only utf8 is supported for now",
    )
  }
  write_bytes_to_file_internal(
    path,
    @ffi.mbt_string_to_utf8_bytes(content, false),
  )
}

///|
fn path_exists_ffi(path : XExternString) -> Bool = "__moonbit_fs_unstable" "path_exists"

///|
fn path_exists_internal(path : String) -> Bool {
  path_exists_ffi(@ffi.string_to_extern(path))
}

///|
fn create_dir_ffi(path : XExternString) -> Int = "__moonbit_fs_unstable" "create_dir_new"

///|
fn create_dir_internal(path : String) -> Unit raise IOError {
  let res = create_dir_ffi(@ffi.string_to_extern(path))
  guard res != -1 else { raise IOError(get_error_message()) }
}

///|
fn read_dir_ffi(path : XExternString) -> Int = "__moonbit_fs_unstable" "read_dir_new"

///|
fn read_dir_internal(path : String) -> Array[String] raise IOError {
  let res = read_dir_ffi(@ffi.string_to_extern(path))
  guard res != -1 else { raise IOError(get_error_message()) }
  @ffi.string_array_from_extern(get_dir_files_ffi())
}

///|
fn is_file_ffi(path : XExternString) -> Int = "__moonbit_fs_unstable" "is_file_new"

///|
fn is_file_internal(path : String) -> Bool raise IOError {
  let res = is_file_ffi(@ffi.string_to_extern(path))
  guard res != -1 else { raise IOError(get_error_message()) }
  res == 1
}

///|
fn is_dir_ffi(path : XExternString) -> Int = "__moonbit_fs_unstable" "is_dir_new"

///|
fn is_dir_internal(path : String) -> Bool raise IOError {
  let res = is_dir_ffi(@ffi.string_to_extern(path))
  guard res != -1 else { raise IOError(get_error_message()) }
  res == 1
}

///|
fn remove_file_ffi(path : XExternString) -> Int = "__moonbit_fs_unstable" "remove_file_new"

///|
fn remove_file_internal(path : String) -> Unit raise IOError {
  let res = remove_file_ffi(@ffi.string_to_extern(path))
  guard res != -1 else { raise IOError(get_error_message()) }
}

///|
fn remove_dir_ffi(path : XExternString) -> Int = "__moonbit_fs_unstable" "remove_dir_new"

///|
fn remove_dir_internal(path : String) -> Unit raise IOError {
  let res = remove_dir_ffi(@ffi.string_to_extern(path))
  guard res != -1 else { raise IOError(get_error_message()) }
}
